﻿// //  ********************************************************************************
// //  Copyright (C) 2016 Sebastian Kenter. All rights reserved.
// // 
// //  Name    : PhotonServerProject | PhotonServerFramework | ClientRequestForwardHandler.cs
// //  Created : 2016 - 03 - 22 16:20
// //  Modified: 2016 - 04 - 18 09:38
// //  ********************************************************************************

using System;
using ExitGames.Logging;
using NHibernate;
using PhotonServerCommon.Entities.Domain;
using PhotonServerCommon.Enums;
using PhotonServerFramework.Implementation.Client;
using PhotonServerFramework.Implementation.Database;
using PhotonServerFramework.Implementation.Server;
using PhotonServerFramework.Interfaces.Client;
using PhotonServerFramework.Interfaces.Server;
using PhotonServerFramework.Interfaces.Telegram;

namespace PhotonServerFramework.Implementation.Handler.Forward
{
    public class ClientRequestForwardHandler : ClientTelegramHandler
    {
        public override ILogger Logger { get; }
        public override Guid HandlerId { get; set; } = Guid.NewGuid();
        public override HandlerTypes HandlerType => HandlerTypes.Request;
        public override byte HandlerCode => (byte) OperationCodes.ForwardRequest;
        public override int HandlerSubCode => (int) OperationSubCodes.None;
        private readonly IServerPeerRegistry _serverPeerRegistry;

        public ClientRequestForwardHandler(ILogger logger, IServerPeerRegistry serverPeerRegistry)
        {
            Logger = logger;
            _serverPeerRegistry = serverPeerRegistry;
        }

        public override bool HandleTelegram(ITelegram telegram, IClientPeer clientPeer)
        {
            bool handled = false;

            Logger.InfoFormat("Trying to handle telegram of type={0} with code={1}, subcode={2}", telegram.Type,
                (OperationCodes) telegram.TelegramCode, (OperationSubCodes) telegram.TelegramSubCode);

            HandlerT handler = null;
            ServerPeerT peer = null;

            ClientPeer tempClientPeer = clientPeer as ClientPeer;

            using (ISession session = SessionManager.OpenSession())
            {
                // Find handler on any server that can handle the telegram
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        Logger.InfoFormat(
                            "Trying to resolve handler from db with parameters: type={0}, code={1}, subcode={2}",
                            HandlerTypes.Request, (OperationCodes) telegram.TelegramCode,
                            (OperationSubCodes) telegram.TelegramSubCode);

                        handler =
                            session.QueryOver<HandlerT>()
                                .Where(h => h.HandlerType == (int) telegram.Type)
                                .And(h => h.HandlerCode == telegram.TelegramCode)
                                .And(h => h.HandlerSubCode == telegram.TelegramSubCode)
                                .And(h => h.ServerHandler == Convert.ToInt32(true))
                                .SingleOrDefault();

                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        Logger.ErrorFormat("Operation failed with: {0}", ex.Message);
                        transaction.Rollback();
                    }
                }

                if (handler != null)
                {
                    Logger.InfoFormat("Found handler on server {0}", handler.ServerId);

                    using (ITransaction transaction = session.BeginTransaction())
                    {
                        try
                        {
                            peer =
                                session.QueryOver<ServerPeerT>()
                                    .Where(c => c.LocalServerId == handler.ServerId).SingleOrDefault();
                        }
                        catch (Exception ex)
                        {
                            Logger.ErrorFormat("Operation failed with: {0}", ex.Message);
                            transaction.Rollback();
                        }
                    }

                    if (peer != null)
                    {
                        Logger.InfoFormat("Found server with id={0} capable to handle telegram. Sending request.",
                            peer.RemotePeerId);
                        ServerPeer serverPeer =
                            _serverPeerRegistry.GetServerPeerById(Guid.Parse(peer.RemotePeerId)) as ServerPeer;

                        if (tempClientPeer != null)
                        {
                            telegram.TelegramParameters.Add((byte) ParameterCodes.ClientPeerIp,
                                tempClientPeer.ClientPeerData.LocalEndPoint.Address.ToString());
                        }
                        else
                        {
                            telegram.TelegramParameters.Add((byte) ParameterCodes.ClientPeerIp, "0.0.0.0");
                        }

                        serverPeer?.SendTelegram(telegram);
                        handled = true;
                    }
                    else
                    {
                        Logger.ErrorFormat("Server not reachable. Aborting.");
                    }
                }
                else
                {
                    Logger.ErrorFormat("No handler found on any server.");
                }

                session.Close();
            }

            return handled;
        }
    }
}
